package fr.jade.fraclite.orb.registry;

import java.net.InetAddress;
import java.util.logging.Logger;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.factory.GenericFactory;
import org.objectweb.fractal.api.type.ComponentType;
import org.objectweb.fractal.api.type.InterfaceType;
import org.objectweb.fractal.api.type.TypeFactory;
import org.objectweb.fractal.util.Fractal;

import fr.jade.fraclite.orb.api.Binder;
import fr.jade.fraclite.orb.api.Name;
import fr.jade.fraclite.orb.api.NamingException;
import fr.jade.fraclite.orb.names.JGroupsName;
import fr.jade.fraclite.util.FracLite;

/**
 * Provides static methods to launch and get access to a Fractal registry.
 */

public class Registry {

  static Logger log = Logger.getLogger(Registry.class.getName());
  /**
   * The default port used to export the naming service interface.
   */

  public final static int DEFAULT_PORT = 1234;

  public final static String DEFAULT_GROUP = "0";
  /**
   * Private constructor (uninstantiable class).
   */

  private Registry () {
  }

  /**
   * Launches a Fractal registry on the local host.
   *
   * @param args a single argument setting the port number where to pick the
   *      naming service interface.
   * @throws Exception if something goes wrong.
   */

  public static void main (final String[] args)
    throws Exception
  {
    // port number to export the registry interface
    int port = DEFAULT_PORT;
    if (args.length == 1) {
      try {
        port = Integer.parseInt(args[0]);
      } catch (NumberFormatException nfe) {
      }
    }
    createRegistry(port);
  }

  /**
   * Creates {@link NamingService} component and exports its server interface.
   *
   * @param port the port number to be used to export the naming service
   *      interface.
   * @throws Exception if something goes wrong.
   */

  public static void createRegistry (final int port) throws Exception {
    Component boot = Fractal.getBootstrapComponent();
    TypeFactory tf = Fractal.getTypeFactory(boot);
    GenericFactory cf = Fractal.getGenericFactory(boot);
    ComponentType type = tf.createFcType(
        new InterfaceType[] {tf.createFcItfType("registry", "fr.jade.fraclite.orb.registry.NamingService",
                             TypeFactory.SERVER, TypeFactory.MANDATORY, TypeFactory.SINGLE)});
      
    Component registry = cf.newFcInstance(type, "primitive", "fr.jade.fraclite.orb.registry.BasicNamingService");
    
    FracLite.getAttributeController(registry).setAttribute("port", String.valueOf(port));
    Fractal.getLifeCycleController(registry).startFc();
    
    System.out.println("Fractal registry is ready.");
  }

  /**
   * Returns a reference to the {@link NamingService} on the local host, for the
   * default {@link #DEFAULT_PORT port}.
   *
   * @return a reference to the {@link NamingService} on the local host, for the
   *      default {@link #DEFAULT_PORT port}.
   * @throws Exception if something goes wrong.
   */

  public static NamingService getRegistry ()
    throws Exception
  {
    String host = InetAddress.getLocalHost().getHostName();
    return getRegistry(host);
  }

  /**
   * Returns a reference to the {@link NamingService} on the given host, for the
   * default {@link #DEFAULT_PORT port}.
   *
   * @param host the host where the naming service is located.
   * @return a reference to the {@link NamingService} on the given host, for the
   *      default {@link #DEFAULT_PORT port}.
   * @throws Exception if something goes wrong.
   */

  public static NamingService getRegistry (final String host) throws Exception {
    return getRegistry(host, DEFAULT_PORT);
  }

  /**
   * Returns a reference to the {@link NamingService} on the given host and
   * port.
   *
   * @param host the host where the naming service is located.
   * @param port the port that was used to export the naming service on the
   *      given host.
   * @return a reference to the {@link NamingService} on the given host and
   *      port.
   * @throws NamingException 
   * @throws Exception if something goes wrong.
   */

  public static NamingService getRegistry (final String host, final int port) throws NamingException {
    // constructs the Fractal RMI binder
    Binder binder;
    try {
      binder = createBinder();
      return getRegistry(host, port, binder);
    } catch (Exception e) {
      e.printStackTrace();
      throw new NamingException("Cannot create binder "+e.toString());
    }
  }

  /**
   * Returns a reference to the {@link NamingService} on the given host, for the
   * default {@link #DEFAULT_PORT port}.
   *
   * @param host the host where the naming service is located.
   * @param binder the binder to be used to create the binding to the naming
   *      service interface.
   * @return a reference to the {@link NamingService} on the given host, for the
   *      default {@link #DEFAULT_PORT port}.
   * @throws NamingException 
   * @throws JonathanException if something goes wrong.
   */

  public static NamingService getRegistry (final String host, final Binder binder) throws NamingException {
    return getRegistry(host, DEFAULT_PORT, binder);
  }

  /**
   * Returns a reference to the {@link NamingService} on the given host and
   * port.
   *
   * @param host the host where the naming service is located.
   * @param port the port that was used to export the naming service on the
   *      given host.
   * @param binder the binder to be used to create the binding to the naming
   *      service interface.
   * @return a reference to the {@link NamingService} on the given host and
   *      port.
   * @throws NamingException 
   * @throws JonathanException if something goes wrong.
   */

  public static NamingService getRegistry (final String host, final int port, final Binder binder) throws NamingException {
    //Name n = new TcpIpName(0, "fr.jade.fraclite.orb.registry.NamingService", binder, host, port);
    Name n = new JGroupsName(0,"fr.jade.fraclite.orb.registry.NamingService", binder, DEFAULT_GROUP);
    System.out.println("[getRegistry]"+n);
    return (NamingService)binder.bind(n,null);
  }
  
  public static Binder createBinder() throws Exception {
    Component boot = Fractal.getBootstrapComponent();
    TypeFactory tf = Fractal.getTypeFactory(boot);
    GenericFactory cf = Fractal.getGenericFactory(boot);
    ComponentType type = tf.createFcType(
        new InterfaceType[] {tf.createFcItfType("binder", "fr.jade.fraclite.orb.api.Binder",
                             TypeFactory.SERVER, TypeFactory.MANDATORY, TypeFactory.SINGLE)});
      
    Component binder = cf.newFcInstance(type, "basic", "fr.jade.fraclite.orb.BasicBinder");
    System.out.println("Binder is ready.");
    return (Binder)binder.getFcInterface("binder");
  }
  
}
